package gao.xiaolei.service;

import com.google.gson.Gson;
import gao.xiaolei.dao.*;
import gao.xiaolei.entity.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import gao.xiaolei.dto.JwtDto;
import gao.xiaolei.util.JwtUtil;
import gao.xiaolei.util.GsonUtil;
import gao.xiaolei.util.RedisUtil;
import gao.xiaolei.util.ServiceName;

import javax.annotation.Resource;
import javax.transaction.Transactional;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Service("AuthService")
public class AuthService {

    @Resource(name = "UserDao")
    private UserDao userDao;

    @Resource(name = "UrlRoleDao")
    private UrlRoleDao urlRoleDao;

    @Resource(name = "UserRoleDao")
    private UserRoleDao userRoleDao;

    @Resource(name = "RoleDao")
    private RoleDao roleDao;

    @Resource(name = "UrlDao")
    private UrlDao urlDao;

    @Resource(name = "GroupDao")
    private GroupDao groupDao;

    @Resource(name = "GroupMemberDao")
    private GroupMemberDao groupMamberDao;

    @Resource(name = "GroupManageDao")
    private GroupManageDao groupManageDao;

    @Resource(name = "AuthResourceDao")
    private AuthResourceDao authResourceDao;

    @Resource(name = "AuthResourceViewDao")
    private AuthResourceViewDao authResourceViewDao;

    @Resource(name = "AuthVersionDao")
    private AuthVersionDao authVersionDao;

    @Resource(name = "GsonUtil")
    private GsonUtil gsonUtil;

    @Resource(name = "RedisUtil")
    private RedisUtil redis;

    @Autowired
    private RestTemplate restTemplate;

    @Resource(name = "PasswordEncoder")
    private PasswordEncoder passwordEncoder;

    //获取用户的昵称
    public String getRealName(String userId) {
        return userDao.findById(userId).get().getRealName();
    }

    //检验用户名是否存在
    public boolean checkUserName(String userName) {
        return userDao.getUserNameCount(userName) > 0;
    }

    //检验用户密码是否正确
    public boolean checkUserNameAndPassword(String userName, String password) {
        return passwordEncoder.matches(password, userDao.getPassword(userName));
    }

    //用户登录
    @Transactional
    public JwtDto userLoginProcess(String userName, String password) {
        if (checkUserName(userName)) {
            if (checkUserNameAndPassword(userName, password)) {
                User user = userDao.findByUsername(userName);
                AuthVersion av = authVersionDao.findById(user.getId()).orElse(null);
                if (av == null) {//登陆更新登陆时间
                    av = new AuthVersion(user.getId(), 0, LocalDateTime.now());
                    authVersionDao.save(av);
                } else
                    authVersionDao.updateLoginTime(LocalDateTime.now(), user.getId());
                if (!redis.hasKey(user.getId()))
                    redis.set(user.getId(), av.getVersion(), redis.EXPIRETIME);//将权限版本号储存到缓存
                return new JwtDto(200, createJwt(user.getId(), av.getVersion()));
            } else
                return new JwtDto(400, null);//密码不正确
        } else
            return new JwtDto(401, null);//账户不存在
    }

    //用户注册
    @Transactional
    public Integer userRegister(String id, String userName, String realName, String password) {
        if (!checkUserName(userName)) {
            userRoleDao.save(new UserRole(id, roleDao.getIdByName("ROLE_BASE"))); //每个用户都有的基础角色
            userRoleDao.save(new UserRole(id, roleDao.getIdByName("ROLE_USER"))); //默认用户注册角色为用户
            User u = new User(id, userName, realName, passwordEncoder.encode(password));
            userDao.save(u);
            return 1;
        } else
            return 0;
    }

    //刷新token
    @Transactional
    public JwtDto refreshToken(String userId) {
        if (userDao.findById(userId).isPresent()) {
            return new JwtDto(200, createJwt(userId, authVersionDao.findById(userId).get().getVersion()));
        } else
            return new JwtDto(401, null);//账户不存在
    }

    //获取某个用户能够管理的用户(比如能够想什么人发任务)
    @Transactional
    public List<User> getBranch(String access_token) {
        //List<User> result = new LinkedList<User>();
        String userId = JwtUtil.getUserId(access_token);
        String[] roles = JwtUtil.getRoles(access_token);
        // 如果是超级管理员的话就把所有管理员的名单加入列表中
        if (Arrays.stream(roles).filter(r -> "ROLE_SUPERADMIN".equals(r)).count() != 0) {
            List<String> roleNames = new ArrayList<>();
            roleNames.add("ROLE_ADMIN");
            List<String> roleIds = roleDao.getAllIdByNameIn(roleNames);
            List<String> userIds = userRoleDao.getUserByRoleIdIn(roleIds).stream().distinct().collect(Collectors.toList());//可能查出重复用户，要去重
            //result.addAll(userDao.findAllById(userIds));
            return userDao.findAllById(userIds);
        }
        // 如果该用户是管理员的话就直接给所有测试主任
        if (Arrays.stream(roles).filter(r -> "ROLE_ADMIN".equals(r)).count() != 0) {
            List<String> roleNames = new ArrayList<>();
            roleNames.add("ROLE_DIRECTOR");
            List<String> roleIds = roleDao.getAllIdByNameIn(roleNames);
            List<String> userIds = userRoleDao.getUserByRoleIdIn(roleIds).stream().distinct().collect(Collectors.toList());//可能查出重复用户，要去重
            //result.addAll(userDao.findAllById(userIds));
            return userDao.findAllById(userIds);
        }
        // 如果是测试主管的话就把所有培训老师和测试经理的名单加入列表中
        if (Arrays.stream(roles).filter(r -> "ROLE_DIRECTOR".equals(r)).count() != 0) {
            List<String> roleNames = new ArrayList<>();
            roleNames.add("ROLE_MANAGE");
            roleNames.add("ROLE_TEACHER");
            List<String> roleIds = roleDao.getAllIdByNameIn(roleNames);
            List<String> userIds = userRoleDao.getUserByRoleIdIn(roleIds).stream().distinct().collect(Collectors.toList());//可能查出重复用户，要去重
            //result.addAll(userDao.findAllById(userIds));
            return userDao.findAllById(userIds);
        }
        // 如果他还是培训老师或者测试经理的话就把他们管理的组的组长名单加入到列表中
        if (Arrays.stream(roles).filter(r -> "ROLE_DIRECTOR".equals(r) || "ROLE_TEACHER".equals(r)).count() != 0) {
            List<User> result = new LinkedList<>();
            List<String> groupIds = groupManageDao.queryGroupByManage(userId);
            for (int i = 0, length = groupIds.size(); i < length; i++) {
                String leader = groupMamberDao.queryGroupLeader(groupIds.get(i));
                if (leader != null)
                    result.add(userDao.findById(leader).get());//直接get的话如果为null的话Optional会报错
            }
            return result;
        }
        // 如果他是组长的话就把他所在组的组员的名单加入到列表中
        if (Arrays.stream(roles).filter(r -> "ROLE_GROUP_LEADER".equals(r)).count() != 0) {
            List<User> result = new LinkedList<>();
            List<String> groupIds = groupMamberDao.queryGroupByLeader(userId);
            for (int i = 0, length = groupIds.size(); i < length; i++) {
                List<String> members = groupMamberDao.queryGroupMember(groupIds
                        .get(i));
                members.remove(userId);// 把自己剔除
                for (int j = 0, length2 = members.size(); j < length2; j++) {
                    result.add(userDao.findById(members.get(j)).get());
                }
            }
            return result;
        }
        return new ArrayList<>(0);
    }

    //获取某个人管理的组
    @Transactional
    public String getBranchGroup(String access_token) {
        String userId = JwtUtil.getUserId(access_token);
        List<String> groupIds = groupManageDao.queryGroupByManage(userId);
        List<Groupcs> groups = new ArrayList<Groupcs>(groupIds.size());
        for (int i = 0, length = groupIds.size(); i < length; i++) {
            groups.add(groupDao.findById(groupIds.get(i)).get());
        }
        return new Gson().toJson(groups);
    }

    @Transactional
    //用于生成Jwt
    public String createJwt(String userId, Integer version) {
        User user = userDao.findById(userId).get();//获取用户信息
        List<String> roleIds = userRoleDao.getRoleByUserId(user.getId());
        List<Role> roles = roleDao.findAllById(roleIds);
        Set<String> urls = new HashSet<String>();
        for (int i = 0, length = roles.size(); i < length; i++) {
            List<String> urlIds = urlRoleDao.queryUrlByRoleId(roles.get(i).getId());
            for (int j = 0, length2 = urlIds.size(); j < length2; j++) {
                Url u = urlDao.findById(urlIds.get(j)).orElse(new Url(null, " ", null, null));
                urls.add(u.getUrl().concat("---").concat(String.valueOf(u.getReadOnly())));//获取用户对应角色拥有的权限点
            }
        }
        List<String> leaderInGroups = groupMamberDao.queryGroupByLeader(userId);
        List<String> leaderProjectIds = new LinkedList<>();//获取是哪些项目的组长
        for (int i = 0, length = leaderInGroups.size(); i < length; i++) {
            List<String> projectIds = restTemplate.getForObject(ServiceName.TEST_SERVICE.getServiceName().concat("/testProject/group/auth/").concat(leaderInGroups.get(i)), List.class);
            if (projectIds.size() > 0)
                leaderProjectIds.addAll(projectIds);
        }
//        Map<String, String[]> authResourceMap = new HashMap<>();
//        Set<String> authUrls = new HashSet<>();
//        List<AuthResourceView> authResourceViewList = authResourceViewDao.findByUserId(user.getId());//获取用户被临时分配的权限信息
//        for (int i = 0, length = authResourceViewList.size(); i < length; i++) {
//            authUrls.add(authResourceViewList.get(i).getUrl());//将被赋予的权限点储存到set里面
//        }
//        for (String authUrl : authUrls) {//遍历set获取每个被赋予权限点对应的资源
//            List<String> resources = new ArrayList<>();
//            for (int i = 0, length = authResourceViewList.size(); i < length; i++) {
//                if (authResourceViewList.get(i).getUrl().equals(authUrl))
//                    resources.add(authResourceViewList.get(i).getResourceId());
//            }
//            authResourceMap.put(authUrl, (String[]) resources.toArray());//将权限点和对应的资源形成一个map
//        }
        return JwtUtil.sign(userId, listToArray(roles), projectListToArray(leaderProjectIds), setToArray(urls), new HashMap<>(0), version);
    }

    private String[] projectListToArray(List<String> groupList) {
        String[] array = new String[groupList.size()];
        return groupList.toArray(array);
    }

    private String[] listToArray(List<Role> roleList) {
        String[] roles = new String[roleList.size()];
        for (int i = 0, length = roleList.size(); i < length; i++) {
            roles[i] = roleList.get(i).getName();
        }
        return roles;
    }

    private String[] setToArray(Set<String> urls) {
        String[] urlArray = new String[urls.size()];
        int i = 0;
        for (String url : urls) {
            urlArray[i] = url;
            i++;
        }
        return urlArray;
    }
}
